﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BRM.DataObjects;
using BRM.DataAccess;
using System.IO;
using System.Data.SqlClient;
using Microsoft.SqlServer.Management;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using System.Collections;
using System.Security.Cryptography;
using System.Configuration;

namespace BRM.Business
{
    public class BackupController
    {
        public delegate void DoProcess(string sFileName);
        public static event DoProcess DoProcessHandler;
        
        /// <summary>
        /// Hàm backup dùng để backup File/Folder
        /// </summary>
        /// <param name="sFilename">Tên file cần backup</param>
        /// <param name="sFilepath">Đường dẫn chứa file backup</param>
        /// <param name="sPassword">Password của file backup</param>
        /// <param name="sVersioninfopath">XML file chứa danh sách File/Folder cần backup</param> 
        public void Backup(string sFilename, string sFilepath, string sPassword, string sVersioninfopath)
        {
            ConfigHelper conf = new ConfigHelper();
            IOHelper newIO = new IOHelper();

            newIO.DoProcessHandler += new IOHelper.DoProcess(newIO_DoProcessHandler);
            //Đọc file config
            conf.ReadConfig("BRM.config");
            /*
             * Nếu file name rỗng thì lấy thông tin từ file config
             * Nếu đường dẫn rỗng thì lấy thông tin từ file config
             * Nếu server không có password thấy password từ file config
             */
            if (sFilename == null) sFilename = conf.sName;
            if (sFilepath == null) sFilepath = conf.sPath;
            if (sPassword == null) sPassword = conf.sServerpass;
            if (Directory.Exists(sFilepath + "\\" + sFilename) == false)
            {
                //
                Directory.CreateDirectory(sFilepath + "\\" + sFilename);
            }

            VERSION u1 = AQFramework.Utilities.Serializer<VERSION>.Load(sVersioninfopath);
            versionItems vi = (versionItems)u1.versions[0];
            /*
             * tạo ra 1 cái folder để chứ các file/folder của chương trình backup
            */
            DirectoryInfo dest = new DirectoryInfo(sFilepath + "\\" + sFilename);
            //Copy file vào folder của chương trình backup
            newIO.CopyFile(vi, dest);
            //Copy folder vào folder của chương trình backup
            newIO.CopyFolder(vi, dest);

            newIO.Compress(sFilepath, sFilepath + "\\" + sFilename, sFilename + ".zip", sPassword);
            
            newIO.ClearAttributes(sFilepath + "\\" + sFilename);
            Directory.Delete(sFilepath + "\\" + sFilename, true);
        }

        void newIO_DoProcessHandler(string sFileName)
        {
            //throw new NotImplementedException();
            if (DoProcessHandler != null) DoProcessHandler(sFileName);
        }
        /// <summary>
        /// Hàm dùng để backup Database
        /// </summary>
        /// <param name="sDbName">Tên database</param>
        /// <param name="sUserName">Tên user</param>
        /// <param name="sPassword">Password user</param>
        /// <param name="sServerName">Tên server</param>
        /// <param name="sDestinationPath">Đường dẫn để lưu file database</param>
        public void BackupDatabase(String sDbName, String sUserName,
            String sPassword, String sServerName, String sDestinationPath)
        {         
            Backup sqlBackup = new Backup();

            sqlBackup.Action = BackupActionType.Database;
            sqlBackup.BackupSetDescription = "ArchiveDataBase:" + DateTime.Now.ToShortDateString();
            sqlBackup.BackupSetName = "Archive";

            sqlBackup.Database = sDbName;
            /*
             * Khởi tạo liên kết với SQL server
             */
            BackupDeviceItem deviceItem = new BackupDeviceItem(sDestinationPath, DeviceType.File);
            ServerConnection connection = new ServerConnection(sServerName, sUserName, sPassword);
            Server sqlServer = new Server(connection);

            Database db = sqlServer.Databases[sDbName];

            sqlBackup.Initialize = true;
            sqlBackup.Checksum = true;
            sqlBackup.ContinueAfterError = true;

            sqlBackup.Devices.Add(deviceItem);
            sqlBackup.Incremental = false;

            sqlBackup.ExpirationDate = DateTime.Now.AddDays(3);
            sqlBackup.LogTruncation = BackupTruncateLogType.Truncate;

            sqlBackup.FormatMedia = false;
            //Tiến hành backup
            sqlBackup.SqlBackup(sqlServer);
            if (DoProcessHandler != null)
                DoProcessHandler(sDbName);
        }
    
        /// <summary>
        /// Hàm dùng để ghi lại thông tin của việc backup vào file XML versionmanagement
        /// </summary>
        /// <param name="sname">Tên của file backup</param>
        /// <param name="sdate">Ngày backup</param>
        /// <param name="spath">Đường dẫn lưu file backup</param>
        /// <param name="sPass">Password của file backup</param>
        /// <param name="sbk_date">Ngày backup tự động</param>
        /// <param name="sbk_time">Thời gian backup tự động</param>
        /// <param name="svmanage_path">Đường dẫn lưu file versionmanagment.xml</param>
        public void Update_Version(string sname, string sdate, string spath,string sPass, string sbk_date, string sbk_time, string svmanage_path)
        {
            //load file versionmanagement
            VERSIONMANAGEMENT vm = AQFramework.Utilities.Serializer<DataObjects.VERSIONMANAGEMENT>.Load(svmanage_path);
            versioninfo vinfo = new versioninfo();
            //update thông tin vô file version.xml
            vinfo.date = DateTime.Now.ToShortDateString();
            vinfo.version = DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString() + DateTime.Now.Year.ToString();
            vinfo.name = sname;
            vinfo.sPassword =Encrypt(sPass,true);
            vinfo.path = spath;
            vinfo.bkdate = sbk_date;
            vinfo.bktime = sbk_time;
            //Add vô mảng version item và save lại file versionmanagement.xml
            vm.versioninfo.Add(vinfo);
            AQFramework.Utilities.Serializer<DataObjects.VERSIONMANAGEMENT>.Save(vm, svmanage_path);
        }

        /// <summary>
        /// Hàm ghi nhận thông tin sau mỗi lần backup database
        /// </summary>
        /// <param name="sName">Tên database</param>
        /// <param name="sFname">Tên file backup</param>
        /// <param name="sDate">Ngày backup</param>
        /// <param name="sPath">Đường dẫn lưu file backup</param>
        /// <param name="sBk_date">Ngày backup tự động</param>
        /// <param name="sBk_time">Giờ backup tự động</param>
        /// <param name="sVmanage_path">Đường dẫn lưu file versionmanagement.xml</param>
        public void Update_DBVersion(string sName, string sFname, string sDate, string sPath, string sBk_date, string sBk_time, string sVmanage_path)
        {
            //load folder versionmanagement
            VERSIONMANAGEMENT vm = AQFramework.Utilities.Serializer<DataObjects.VERSIONMANAGEMENT>.Load(sVmanage_path);
            versiondbinfo vinfo = new versiondbinfo();
            vinfo.bkdbdate = DateTime.Now.ToShortDateString();
            vinfo.db_version = DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString() + DateTime.Now.Year.ToString();
            vinfo.dbname = sName;
            vinfo.sfname = sFname;
            vinfo.dbpath = sPath;
            vinfo.bk_dbdate = sBk_date;
            vinfo.bk_dbtime = sBk_time;
            //Add vô mảng version item và save lại file versionmanagement.xml
            vm.versiondbinfo.Add(vinfo);
            AQFramework.Utilities.Serializer<DataObjects.VERSIONMANAGEMENT>.Save(vm, sVmanage_path);
        }
        /// <summary>
        /// Hàm tạo file info kèm theo mỗi file backup
        /// </summary>
        /// <param name="arrFileinfo">Mảng chứa thông tin file cần backup</param>
        /// <param name="arrFolderinfo">Mảng chứa thông tin folder cần backup </param>
        /// <param name="sDateinfo">Ngày backup</param>
        /// <param name="sNameinfo">Tên file backup</param>
        /// <param name="sPathinfo"> Đường dẫn của file backup</param>
        /// <param name="sPass">Password của file backup</param>
        /// <param name="sBkdateinfo">Ngày backup tự động</param>
        /// <param name="sBktimeinfo">Giờ backup tự động</param>
        /// <param name="sInfopath">Đường dẫn file info.xml</param>
        public void Create_info(ArrayList arrFileinfo, ArrayList arrFolderinfo, string sDateinfo, string sNameinfo, string sPathinfo, string sPass,string sBkdateinfo, string sBktimeinfo, string sInfopath)
        {
            VERSION u = new VERSION();
            AQFramework.Utilities.Serializer<DataObjects.VERSION>.Save(u, sInfopath);
            u = AQFramework.Utilities.Serializer<VERSION>.Load(sInfopath);
            versionItems vi = new versionItems();

            foreach (string sFile in arrFileinfo)
            {
                FileInfo f = new FileInfo(sFile);
                fileItems fi = new fileItems();

                fi.name = f.Name;
                fi.path = sFile;
                fi.ext = f.Extension;
                fi.size = f.Length.ToString();

                vi.detail.files.Add(fi);
            }
            foreach (string sFolder in arrFolderinfo)
            {
                FileInfo f1 = new FileInfo(sFolder);
                folderItems foi = new folderItems();

                foi.name = f1.Name;
                foi.path = sFolder;
                DirectoryInfo dir = new DirectoryInfo(sFolder);
                foi.size = GetDirectorySize(dir).ToString();
                vi.detail.folders.Add(foi);
            }
            vi.name = sNameinfo;
            vi.date = sDateinfo;
            vi.sPassword = Encrypt(sPass,true);
            vi.path = sPathinfo;
            vi.bkdate = sBkdateinfo;
            vi.bktime = sBktimeinfo;
            u.versions.Add(vi);
            AQFramework.Utilities.Serializer<VERSION>.Save(u, sInfopath);
        }
        /// <summary>
        /// Hàm lấy kích cỡ của folder
        /// </summary>
        /// <param name="d">Đường dẫn đến folder</param>
        /// <returns>Trả ra kích cỡ của folder</returns>
        public long GetDirectorySize(DirectoryInfo d)
        {

            long Size = 0;
            // Lấy độ lớn cho file
            FileInfo[] fis = d.GetFiles();
            foreach (FileInfo fi in fis)
            {
                Size += fi.Length;
            }
            // Lấy độ lớn cho những file/folder con
            DirectoryInfo[] dis = d.GetDirectories();
            foreach (DirectoryInfo di in dis)
            {
                Size += GetDirectorySize(di);
            }
            return (Size);
        }
        /// <summary>
        /// Hàm để mã hóa password người dùng truyền vào bằng cách sử dụng mã hash
        /// </summary>
        /// <param name="sToEncrypt">Chuỗi password cần mã hóa</param>
        /// <param name="UserHashing">Có sử dụng kiểu hash hay không; True là có, False là không</param>
        /// <returns>Trả ra chuỗi kí tự được mã hóa</returns>
        public string Encrypt(string sToEncrypt, bool UserHashing)
        {
            byte[] keyArray;
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(sToEncrypt);

            System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
            // Lấy key từ file config
            string sKey = (string)settingsReader.GetValue("SecurityKey", typeof(String));

            if (UserHashing)
            {
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(sKey));
                hashmd5.Clear();
            }
            else
                keyArray = UTF8Encoding.UTF8.GetBytes(sKey);

            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            tdes.Key = keyArray;
            tdes.Mode = CipherMode.ECB;
            tdes.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = tdes.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            tdes.Clear();
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
    }
}


